#include "stdafx.h"
#include "global.h"
#include "ddfunc.h"
#include "convcolors.h"

/********************************************************************
* DDrawInit initializes Direct Draw and create main surfaces        *
* hWnd - the window this function is loaded from		(in)        *
********************************************************************/

HRESULT DDrawInit(HWND hWnd)
{
	HRESULT hr;
	hr = DirectDrawCreateEx(NULL, (VOID**) &GV.pIDD,
		IID_IDirectDraw7, NULL);
	if (FAILED(hr))
		return hr;
	RECT wndRect;
	RECT clientRect;
	GetWindowRect(hWnd, &wndRect);
	GetClientRect(hWnd, &clientRect);
	int wndWidth = 176 + (wndRect.right-wndRect.left) -
		(clientRect.right-clientRect.left);
	int wndHeight = (wndRect.bottom-wndRect.top) -
		(clientRect.bottom-clientRect.top);
	MoveWindow(hWnd,wndRect.left,wndRect.top,
		wndWidth,wndHeight,TRUE);
	GV.pIDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
	DDSURFACEDESC2 desc;
	ZeroMemory(&desc, sizeof(desc));
	desc.dwSize = sizeof(desc);
	desc.dwFlags = DDSD_CAPS;
	desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	hr = GV.pIDD->CreateSurface(&desc, &GV.primsurf,0);
	if (FAILED(hr))
		return hr;
	ZeroMemory(&desc, sizeof(desc));
	desc.dwSize = sizeof(desc);
	desc.dwFlags  = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
	GetClientRect(hWnd, &clientRect);
	desc.dwWidth  = clientRect.right  - clientRect.left;
	desc.dwHeight = 1;
	desc.ddsCaps.dwCaps = 
		DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
	hr = GV.pIDD->CreateSurface(&desc, &GV.backsurf,0);
	if (FAILED(hr))
		return hr;
	GV.mainsurf = 0;
	return S_OK;
}

HRESULT LoadPicture(LPDIRECTDRAWSURFACE7 *surf, DWORD Resource, int flags)
{
	HANDLE hBmp = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(Resource), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
	BITMAP bm;
	if (hBmp == 0) 
		return FALSE;
	GetObject(hBmp,sizeof(bm), &bm);
	DDSURFACEDESC2 desc;
	ZeroMemory(&desc, sizeof(desc));
	desc.dwSize = sizeof(desc);
	desc.dwFlags = DDSD_WIDTH |DDSD_HEIGHT | DDSD_CAPS;

	desc.dwWidth = bm.bmWidth;
	desc.dwHeight = bm.bmHeight;

	desc.ddsCaps.dwCaps = 
		DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
	HRESULT r= GV.pIDD->CreateSurface(&desc, surf, 0);
	if (FAILED(r))
		return r;
	HDC hdcImage;
	hdcImage = CreateCompatibleDC(0);
	if (hdcImage == 0)
		return FALSE;
	SelectObject(hdcImage,hBmp);
	HDC hdc;
	(*surf)->GetDC(&hdc);
	StretchBlt(hdc,0,0,desc.dwWidth,desc.dwHeight,
		hdcImage,0,0,bm.bmWidth, bm.bmHeight, SRCCOPY);
	(*surf)->ReleaseDC(hdc);
	DeleteDC(hdcImage);
	DeleteObject(hBmp);

	return S_OK;
}

/********************************************************************
* ClearDDraw releases ddraw files (at least it should do it)        *
********************************************************************/

HRESULT ClearDDraw()
{
	if (GV.mainsurf){	GV.mainsurf->Release();	GV.mainsurf = 0;}
	if (GV.waitsurf){	GV.waitsurf->Release();	GV.waitsurf = 0;}
	if (GV.backsurf){	GV.backsurf->Release();	GV.backsurf = 0;}
	if (GV.primsurf){	GV.primsurf->Release();	GV.primsurf = 0;}
	if (GV.pIDD)	{	GV.pIDD->Release();		GV.pIDD = 0;	}
	return S_OK;
}

/*******************************************************************\
* LoadVideo - get file and put the content into VideoFile struct    *
*	*vid - pointer to the VideoFile struct				(out)       *
*	hWnd - the Window, that the function is loaded from	(in)        *
*	Dialog - If the function gets the file from dialog              *
*		or from inner string							(in)        *
\*******************************************************************/

HRESULT LoadVideo(VideoFile *vid, HWND hWnd, bool Dialog)
{

	HANDLE hand;
	if (Dialog) 
		hand = GetOpenFile(hWnd);
	else
		hand = CreateFile(GV.FileName,GENERIC_READ,0, 0,OPEN_EXISTING,0,0);

	if (hand == 0)
	{
		vid->iWidth = 176;
		vid->iHeight = 0;
	} 
	else if ((vid->iWidth == 0) || (vid->iHeight == 0))
	{
		vid->iWidth  = 176;
		vid->iHeight = 144;
	}
	DWORD w = vid->iWidth;
	DWORD h = vid->iHeight;
	HRESULT hr;
	RECT wndRect, clientRect;
	GetWindowRect(hWnd, &wndRect);
	GetClientRect(hWnd, &clientRect);
	int wndWidth = w + (wndRect.right-wndRect.left) -
		(clientRect.right-clientRect.left);
	int wndHeight = h + (wndRect.bottom-wndRect.top) -
		(clientRect.bottom-clientRect.top);
	MoveWindow(hWnd,wndRect.left,wndRect.top,
		wndWidth,wndHeight,TRUE);
	if (hand == 0) return 0;

	GV.backsurf->Release();
	if (GV.mainsurf) GV.mainsurf->Release();
	DDSURFACEDESC2 desc;
	ZeroMemory(&desc, sizeof(desc));
	desc.dwSize = sizeof(desc);
	desc.dwFlags  = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
	GetClientRect(hWnd, &clientRect);
	desc.dwWidth  = w;
	desc.dwHeight = h;
	desc.ddsCaps.dwCaps = 
		DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
	hr = GV.pIDD->CreateSurface(&desc, &GV.backsurf,0);
	if (FAILED(hr))
		return hr;

	RECT dest;
	SetRect(&dest,0,0,w,h);
	GV.backsurf->Blt(&dest, GV.waitsurf, 0, DDBLT_WAIT, 0);
	POINT p;
	p.x = 0; p.y = 0;
	ClientToScreen(hWnd, &p);
	GetClientRect(hWnd, &clientRect);
	OffsetRect(&clientRect,p.x,p.y);
	GV.primsurf->Blt(&clientRect,GV.backsurf,0,DDBLT_WAIT,0);

	ZeroMemory(&desc, sizeof(desc));
	desc.dwSize = sizeof(desc);
	desc.dwFlags  = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
	desc.ddsCaps.dwCaps = 
		DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
	desc.dwWidth  = w;
	desc.dwHeight = h;
	hr = GV.pIDD->CreateSurface(&desc, &GV.mainsurf,0);

	DWORD read;
	BYTE *buff;
	DWORD FileSize = GetFileSize(hand,0);
	vid->iWidth= w;
	vid->iHeight = h;
	vid->nFrames = FileSize / (vid->iWidth * vid->iHeight) * 8 / 12;
	GV.Frame = 0;
	buff = new BYTE [FileSize];
	if (vid->Content) delete[] vid->Content;
	vid->Content = new DWORD[vid->nFrames * vid->iWidth * vid->iHeight];
	ReadFile(hand,buff,FileSize,&read, NULL);
	DWORD Offset, Offset2;
	BYTE _1, _2, _3;
	for (DWORD k = 0; k < vid->nFrames;k++)
	{
		Offset = k * w * h / 2 * 3;
		Offset2 = k * w * h;
		for (DWORD i = 0; i < h; i++)
		{
			for (DWORD j = 0; j < w; j++)
			{
				_1 = buff[Offset + i * w + j];
				_2 = buff[Offset + w * h + (i / 2) * (w / 2) + (j / 2)];
				_3 = buff[Offset + (w * h) / 4 * 5 + (i / 2) * (w / 2) + (j / 2)];
				if (GV.VType  == VTYPE_YUV)
					vid->Content[Offset2 + i * w + j] = YUVtoRGB32(_1,_2,_3);
				else if (GV.VType == VTYPE_HSB)
				{
				//	vid->Content[Offset2 + i * w + j] = HSBtoRGB32(_2,_3,_1);
					vid->Content[Offset2 + i * w + j] = YUVtoRGB32(_1,_2,_3);
				}
			}
		}
	}
	delete[] buff;
	CloseHandle(hand);
	return S_OK;
}

HANDLE GetOpenFile(HWND hWnd)
{
	OPENFILENAME oFileName;
	if (GV.FileName) delete[] GV.FileName;
	GV.FileName = new WCHAR[256];
	ZeroMemory(&oFileName, sizeof(oFileName));
	oFileName.lStructSize = OPENFILENAME_SIZE_VERSION_400;
	oFileName.hwndOwner = hWnd;
	oFileName.lpstrFilter = L"YUV sequences\0.yuv;*.YUV\0HSB sequence\0*.hsb;*.HSB\0";
	oFileName.lpstrFile = GV.FileName;
	oFileName.lpstrFile[0] = 0;
	// Size of filename in TCHARS
	oFileName.nMaxFile = 256;
	oFileName.lpstrInitialDir = GV.OpenSaveDir;
	oFileName.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST;
	BOOL r = GetOpenFileName(&oFileName);
	if (!r) return 0;
	// Saving file directory for future Save/Load
	WORD i = 0;
	if (GV.OpenSaveDir) delete[] GV.OpenSaveDir;
	GV.OpenSaveDir = new WCHAR[oFileName.nFileOffset + 1];
	while (i < oFileName.nFileOffset)
	{
		GV.OpenSaveDir[i] = GV.FileName[i];
		i++;
	}
	GV.OpenSaveDir[i] = 0;
	if (oFileName.nFilterIndex == 1) GV.VType = VTYPE_YUV;
	if (oFileName.nFilterIndex == 2) GV.VType = VTYPE_HSB;
	HANDLE hand = CreateFile(GV.FileName,GENERIC_READ,0, 0,OPEN_EXISTING,0,0);
	return hand;
}

int SaveFile(HWND hWnd)
{
	OPENFILENAME oFileName;
	if (GV.FileName) delete[] GV.FileName;
	GV.FileName = new WCHAR[256];
	ZeroMemory(&oFileName, sizeof(oFileName));
	oFileName.lStructSize = OPENFILENAME_SIZE_VERSION_400;
	oFileName.hwndOwner = hWnd;
	oFileName.lpstrFilter = L"YUV sequences\0.yuv;*.YUV\0HSB sequence\0*.hsb;*.HSB\0";
	oFileName.lpstrFile = GV.FileName;
	oFileName.lpstrFile[0] = 0;
	// Size of filename in TCHARS
	oFileName.nMaxFile = 256;
	oFileName.lpstrInitialDir = GV.OpenSaveDir;
	oFileName.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST;
	BOOL r = GetSaveFileName(&oFileName);
	if (!r) return -1;
	// Saving file directory for future Save/Load
	WORD i = 0;
	if (GV.OpenSaveDir) delete[] GV.OpenSaveDir;
	GV.OpenSaveDir = new WCHAR[oFileName.nFileOffset + 1];
	while (i < oFileName.nFileOffset)
	{
		GV.OpenSaveDir[i] = GV.FileName[i];
		i++;
	}
	GV.OpenSaveDir[i] = 0;
	enum VideoType Type;
	if (oFileName.nFilterIndex == 1) Type = VTYPE_YUV;
	if (oFileName.nFilterIndex == 2) Type = VTYPE_HSB;
	while (GV.FileName[i] != 0)
		i++;
	if ((i - oFileName.nFileOffset) < 5)
	{
		LPCWSTR Ext;
		if (Type == VTYPE_YUV)
			Ext = L".yuv";
		if (Type == VTYPE_HSB)
			Ext = L".hsb";
		for (int j = 0; j < 5;j++)
		{
			GV.FileName[i + j] = Ext[j];
		}
	}
	for (int j = 0; j < 4; j ++)
	{
		LPCWSTR Ext;
		if (Type == VTYPE_YUV)
			Ext = L".yuv";
		if (Type == VTYPE_HSB)
			Ext = L".hsb";
		if (GV.FileName[i - 4 + j] != Ext[j])
		{
			for (int k = 0; k < 5;k++)
			{
				GV.FileName[i + k] = Ext[k];
			}
			j = 5;
		}
	}

	HANDLE hand = CreateFile(GV.FileName,GENERIC_WRITE,0, 0, CREATE_ALWAYS,0,0);
	if (hand) SaveVideo(hand, &Type);
	CloseHandle(hand);
	return 0;
}

HRESULT ReleaseVideo(VideoFile *vid)
{
	if (vid->Content) delete[] vid->Content;
	ZeroMemory(vid, sizeof(*vid));
	return S_OK;
}

HRESULT ShowVideo(HWND hWnd)
{
	if (!GV.mainsurf) return 0;
	DDSURFACEDESC2 desc;
	ZeroMemory(&desc, sizeof(desc));
	desc.dwSize = sizeof(desc);
	if (GV.vid.nFrames == 0)
	{
		if (GV.mainsurf->Lock(0,&desc,DDLOCK_WAIT,0) == DD_OK)
		{
			DWORD *dst = (DWORD *) desc.lpSurface;
			for (DWORD i = 0; i < desc.dwHeight;i++)
			{
				for (DWORD j = 0; j < desc.dwWidth;j++)
				{
					* (DWORD *)(dst + j) =	0xff000000;
				}
				dst = dst +desc.lPitch / 4;
			}
			GV.mainsurf->Unlock(0);	
		}
		GV.backsurf->BltFast(0,0,GV.mainsurf,0,DDBLTFAST_WAIT);
		RECT clientRect;
		POINT p;
		p.x = 0; p.y = 0;
		ClientToScreen(hWnd, &p);
		GetClientRect(hWnd, &clientRect);
		OffsetRect(&clientRect,p.x,p.y);
		GV.primsurf->Blt(&clientRect,GV.backsurf,0,DDBLT_WAIT,0);
		return E_FAIL;
	}
	DWORD Offset = abs(GV.Frame) * (GV.vid.iWidth * GV.vid.iHeight);
	if (GV.mainsurf->Lock(0,&desc,DDLOCK_WAIT,0) == DD_OK)
	{
		DWORD *dst = (DWORD *) desc.lpSurface;
		DWORD w = 0;
		ColorFrame *Frame;
		for (DWORD i = 0; i < desc.dwHeight;i++)
		{
			for (DWORD j = 0; j < desc.dwWidth;j++)
			{
				// * Check output **********************************/
				DWORD color = GV.vid.Content[w + Offset];
				Frame = (ColorFrame *) &color;
				color = RGBtoHSB32(color);
				if (Frame->_1 < 130 || Frame->_1 > 200)
					Frame->_1 = Frame->_1 & 0xf8;
				else
					Frame->_1 = Frame->_1 & 0xfe;
				color = HSBtoRGB32(color);
				* (DWORD *)(dst + j) = color;
				// Standard output
				//* (DWORD *)(dst + j) = GV.vid.Content[w + Offset];
				w++;
			}
			dst = dst +desc.lPitch / 4;
		}
		GV.mainsurf->Unlock(0);
	}
	if (GV.vid.nFrames != 1) GV.Frame++;
	if (GV.Frame == GV.vid.nFrames) GV.Frame = - (int)GV.vid.nFrames + 2;
	GV.backsurf->BltFast(0,0,GV.mainsurf,0,DDBLTFAST_WAIT);
	RECT clientRect;
	POINT p;
	p.x = 0; p.y = 0;
	ClientToScreen(hWnd, &p);
	GetClientRect(hWnd, &clientRect);
	OffsetRect(&clientRect,p.x,p.y);
	GV.primsurf->Blt(&clientRect,GV.backsurf,0,DDBLT_WAIT,0);
	return S_OK;
}

HRESULT SaveVideo(HANDLE file, enum VideoType *Type)
{
	DWORD written;
	BYTE* buff = new BYTE[GV.vid.iWidth * GV.vid.iHeight * 3 / 2 * GV.vid.nFrames];
	ZeroMemory(buff, GV.vid.iWidth * GV.vid.iHeight * 3 / 2 * GV.vid.nFrames);
	DWORD color;
	ColorFrame *Frame = (ColorFrame *) &color;
	for (DWORD i = 0; i < GV.vid.nFrames; i++)
	{
		BYTE *p1 = buff + GV.vid.iWidth * GV.vid.iHeight * 3 / 2 * i; // Offset to the frame
		BYTE *p2 =   p1 + GV.vid.iWidth * GV.vid.iHeight - 1; // offset to _2 (-1 required for p2++ and p3++ work correctly later)
		BYTE *p3 =   p2 + GV.vid.iWidth * GV.vid.iHeight / 4; // offset to _3
		
		DWORD Offset = GV.vid.iWidth * GV.vid.iHeight * i;
		for (DWORD j = 0; j < GV.vid.iHeight; j++)
		{			
			for (DWORD k = 0; k < GV.vid.iWidth; k++)
			{
				if (k % 2 == 0)
				{
					p2++;
					p3++;
				}
				if (*Type == VTYPE_YUV)
				{
					color = RGBtoYUV32(GV.vid.Content[Offset + GV.vid.iWidth * j + k]);
					color = RGBtoHSB32(color);
					if (Frame->_2 < 32) Frame->_2 = 0;
					Frame->_2 = (Frame->_2 >> 3) << 3;
					Frame->_1 = (Frame->_2 >> 3) << 3;
					color = HSBtoRGB32(color);
					*p1 =  Frame->_1;
					*p2 += Frame->_2 >> 2;
					*p3 += Frame->_3 >> 2;
				}
				else
				{
					color = GV.vid.Content[Offset + GV.vid.iWidth * j + k];
					DWORD ch2 = RGBtoHSB32(color);
					if (((ch2 & 0x0000ff00) >> 8) < 5)
					{
						ch2 = ch2 & 0xff0000ff;
						color = HSBtoRGB32(ch2);
					}
					color = RGBtoYUV32(color);
//					*p1 = 0x000000ff & color;
//					*p2 += ((0x00ff0000 & color) >> 18);
//					*p3 += ((0x0000ff00 & color) >> 10);
					*p1 =  Frame->_1;
					*p2 += Frame->_2 >> 2;
					*p3 += Frame->_3 >> 2;
				}
				p1++;
				
			}
			// p2 and p3 goes 2 times one line
			if (j % 2 == 0)
			{
				p2 -= GV.vid.iWidth / 2;
				p3 -= GV.vid.iWidth / 2;
			}
		} // j
	} // i
	WriteFile(file,buff, (GV.vid.iWidth * GV.vid.iHeight) * 3 / 2 * GV.vid.nFrames, &written,0);
	delete[] buff;
	return S_OK;
}